package com.tiancheng.trade.payment.channel.cmb.util;

import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;

/**
 * 招总行签名工具类
 *
 * @author kellen
 * @date 2024/4/15
 */
@Slf4j
@Component
public class CmbSignUtil {

    @Value(value = "${channel.cmb.privateKeyPath}")
    private String privateKeyPath;

    @Value(value = "${channel.cmb.appKey}")
    private String appKey;

    private final String ALGORITHM = "RSA";
    private final String SIGN_ALGORITHMS = "SHA1WithRSA";
    private final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA";
    private final String DEFAULT_CHARSET = "UTF-8";

    private String privateKey = "";

    /**
     * 签名
     * @param params
     * @return
     * @throws BusinessException
     */
    public String sign(SortedMap params) throws BusinessException {
        String content = this.mapToStr(params);
        if(StringUtils.isEmpty(privateKey)) {
            privateKey = this.getKey(privateKeyPath);
        }
        BusinessErrorInfoEnum.CHANNEL_SIGN_FAIL.assertNotEmpty(privateKey);

        String sign = this.sign(content + appKey, privateKey, true);
        try {
            sign = URLEncoder.encode(sign, "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_SIGN_FAIL, null, e.getMessage(), e);
        }
        return sign;
    }

    /**
     * map to string
     * @param map
     * @return
     */
    public String mapToStr(SortedMap map) {
        Object val = null;
        String key = "";
        Iterator iter = map.entrySet().iterator();
        StringBuilder str = new StringBuilder();
        String res = "";
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            // 获取key
            key = (String) entry.getKey();
            // 获取value
            val = entry.getValue();
            if (null == val) {
                val = "";
            }
            str.append(key).append("=").append(val).append("&");
        }
        if (str.length() > 0) {
            res = str.toString().substring(0, str.toString().length() - 1);
        }
        return res;
    }

    /**
     * 获取私钥
     * @param filePath 路径
     * @return
     */
    private String getKey(String filePath) {
        try {
            BufferedReader br = new BufferedReader(new FileReader(filePath));
            String s = br.readLine();
            String str = "";
            if (s != null && s.charAt(0) == '-') {
                s = br.readLine();
            }
            while (s != null && s.charAt(0) != '-') {
                str += s;
                s = br.readLine();
            }
            return str;
        } catch (IOException e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_SIGN_FAIL, null, e.getMessage(), e);
        }
    }

    /**
     * 生成签名
     * @param content
     * @param privateKey
     * @param rsa2
     * @return
     */
    private String sign(String content, String privateKey, boolean rsa2) {
        try {
            java.security.Security.addProvider(
                    new org.bouncycastle.jce.provider.BouncyCastleProvider()
            );
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
                    Base64.getDecoder().decode(privateKey));
            KeyFactory keyf = KeyFactory.getInstance(ALGORITHM);
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            java.security.Signature signature = java.security.Signature.getInstance(rsa2 ? SIGN_SHA256RSA_ALGORITHMS : SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes(DEFAULT_CHARSET));
            byte[] signed = signature.sign();
            return Base64.getEncoder().encodeToString(signed);
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_SIGN_FAIL, null, e.getMessage(), e);
        }
    }
}
